package com.ece.pfe_rover_pvt_ines_ece_application.view;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.ece.pfe_rover_pvt_ines_ece_application.PFE_ROVER_PVT_INES_ECE_Application;
import com.ece.pfe_rover_pvt_ines_ece_application.R;
import com.ece.pfe_rover_pvt_ines_ece_application.helpers.CsvFileHelper_;
import com.ece.pfe_rover_pvt_ines_ece_application.model.Measurements;
import com.ece.pfe_rover_pvt_ines_ece_application.model.SimpleMeasurement;
import com.parse.CountCallback;
import com.parse.FindCallback;
import com.parse.ParseAnonymousUtils;
import com.parse.ParseException;
import com.parse.ParseObject;
import com.parse.ParseQuery;
import com.parse.ParseQueryAdapter;
import com.parse.ParseUser;
import com.parse.SaveCallback;
import com.parse.ui.ParseLoginActivity;
import com.parse.ui.ParseLoginBuilder;

import android.annotation.SuppressLint;
import android.app.ActionBar;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

/**
 * SdLoader Activity
 * Shows button to load the file, save data to Parse
 * @author mathieubakrim
 *
 */
public class AdmDashboardActivity extends Activity {
	
	public boolean DEBUG_MODE = false;//PFE_ROVER_PVT_INES_ECE_Application.DEBUG;
	public static final String FILE_REP = "/storage/extSdCard/"; //extSdCard/", sdcard;
	public static final String FILE_NAME = "test.csv";
	public static final char SEPARATOR = ';';
	private static final int LOGIN_ACTIVITY_CODE = 100;
	private static final boolean AUTHENTICATION_NEEDED = true;
	private static int ENTRY_COUNTER = 0;
	@SuppressWarnings("unused")
	private static int FILE_NB_ENTRIES = 0;
	private List<String> lines;
	private List<String[]> data;
	private Measurements measurements;
	@SuppressWarnings("unused")
	private String measurementsId = null;
	private Button btSync,
				   btList,
				   btTable,
				   btReferentiel,
				   btGraphs;				  	
	
	
	// Adapter for the Measurements Parse Query
	private ParseQueryAdapter<Measurements> MeasurementsListAdapter;
	private static final boolean PUBLIC_READ_ACCESS = true;
	
	/**
	 * OnCreate (Android method related to Activity class)
	 * Compose the view (layouts, buttons...)
	 */
	@Override
	protected void onCreate(Bundle savedInstanceState) {		
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_adm_dashboard);
		
		//Menu & subtitle
		ActionBar actionBar = getActionBar();
		actionBar.setDisplayHomeAsUpEnabled(true);
		actionBar.setSubtitle(R.string.subtitle_home);
		
		// Fetch the measurementsId from the Extra data
		if (getIntent().hasExtra("ID")) {
			measurementsId = getIntent().getExtras().getString("ID");
		}
		
		// Shows up authentication window
		if(AUTHENTICATION_NEEDED){
			if(ParseUser.getCurrentUser() == null) {
				Intent i = new Intent(this, ParseLoginActivity.class);
				startActivityForResult(i, LOGIN_ACTIVITY_CODE);	
			}else { 
				Toast.makeText(getBaseContext(), "Welcome "+ParseUser.getCurrentUser().getUsername(), Toast.LENGTH_SHORT).show();
			}
		}
		
		// Link the view button elements
		btSync = (Button) findViewById(R.id.button_synchronisation);
		btTable = (Button) findViewById(R.id.button_tableau);
		btList = (Button) findViewById(R.id.button_list);
		btGraphs = (Button) findViewById(R.id.btGraphs);
		btReferentiel = (Button) findViewById(R.id.btReferentiel);
		
		//When Sync button pressed, read the file & save its content in local & remote DataBase
		btSync.setOnClickListener(new Button.OnClickListener(){
			public void onClick(View v){				 				
				try {
					loadSd();
					saveToParse();
				} catch (IOException e) {					
					e.printStackTrace();
				}
			}
		});	
		
		btTable.setOnClickListener(new Button.OnClickListener() {
			public void onClick(View v) {
				//TODO
				startActivity(new Intent(getBaseContext(),TableActivity.class));
			}
		});
		
		btGraphs.setOnClickListener(new Button.OnClickListener() {
			public void onClick(View v) {
				//TODO
				startActivity(new Intent(getBaseContext(),GraphsActivity.class));
			}
		});
		
		btReferentiel.setOnClickListener(new Button.OnClickListener() {
			public void onClick(View v) {
				//TODO
				startActivity(new Intent(getBaseContext(),ReferentielActivity.class));
			}
		});
		
		btList.setOnClickListener(new Button.OnClickListener() {
			public void onClick(View v) {				
				startActivity(new Intent(getBaseContext(),MeasureActivity.class));
			}
		});
	}

	/**
	 * OnCreateOptionsMenu (Android method related to Activity class)
	 * Inflate the menu
	 */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {		
		// Inflate the menu
		//this adds items to the action bar if it is present.		
		getMenuInflater().inflate(R.menu.action_logout, menu);
		getMenuInflater().inflate(R.menu.activity_main_actions, menu);		
		getMenuInflater().inflate(R.menu.action_login, menu);	
		getMenuInflater().inflate(R.menu.sd_loader, menu);				
		
		return true;
	}

	/**
	 * onOptionsItemSelected (Android method related to Activity class)
	 * Start the activity related to a selected field in the menu
	 */
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {		
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		int id = item.getItemId();
		switch(id) {
		
		case R.id.measurements_list:
			startActivity(new Intent(this.getBaseContext(),MeasureActivity.class));			
			return true;
			
		case R.id.measurements_table:			
			startActivity(new Intent(this.getBaseContext(),TableActivity.class));			
			return true;
		case R.id.measurements_graphs:			
			startActivity(new Intent(this.getBaseContext(),GraphsActivity.class));			
			return true;
		
		case R.id.referentiel:			
			startActivity(new Intent(this.getBaseContext(),ReferentielActivity.class));			
			return true;
		
		case R.id.action_logout:			
			// Log out the current user
			ParseUser.logOut();
			// Create a new anonymous user
			ParseAnonymousUtils.logIn(null);
						
			// Unpin all the current objects
			ParseObject
					.unpinAllInBackground(PFE_ROVER_PVT_INES_ECE_Application.MEASUREMENTS_GROUP_NAME);
			
			//End the activity & go back the the previous one
			finish();
			
			return super.onOptionsItemSelected(item);
			
		case R.id.action_login:
			invalidateOptionsMenu();
			ParseLoginBuilder builder = new ParseLoginBuilder(this);
			startActivityForResult(builder.build(), LOGIN_ACTIVITY_CODE);
			
			return super.onOptionsItemSelected(item);
		
		default:			
				return super.onOptionsItemSelected(item);
		}
	}
	
	/**
	 * onPrepareOptionsMenu
	 */
	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {		
		
		if(DEBUG_MODE) System.out.println("---- Entering onPrepareOptopnsMenu method...");
		
		boolean realUser;
		super.onPrepareOptionsMenu(menu);
		if(ParseUser.getCurrentUser() != null){
			realUser = !ParseAnonymousUtils.isLinked(ParseUser
				.getCurrentUser());
		
			menu.findItem(R.id.action_login).setVisible(!realUser);						
			
			//Only registered user can see these buttons
			menu.findItem(R.id.action_logout).setVisible(realUser);	
			menu.findItem(R.id.measurements_table).setVisible(realUser);
			menu.findItem(R.id.measurements_graphs).setVisible(realUser);
			menu.findItem(R.id.measurements_list).setVisible(realUser);
			menu.findItem(R.id.referentiel).setVisible(realUser);
		}					
		return true;
	}

	
	/**
	 * Load data from external storage (Sd card)
	 * @throws IOException
	 */
	@SuppressLint("UseValueOf")
	public void loadSd() throws IOException{		
		if(DEBUG_MODE) System.out.println("---- Entering LoadSd method...");
		
		//File sdLien = Environment.getExternalStorageDirectory();
		//previous method does not work for Galaxy Tab 4 device (redirects to local storage/emulated)
		File file = new File(FILE_REP + FILE_NAME);		
		
		if((DEBUG_MODE)) System.out.println("Acessing repositry : "+FILE_REP + FILE_NAME);
		
		if(!file.exists()){
			throw new RuntimeException("Fichier innexistant sur la carte SD");
		}
			
		// CSV read algorithm
		lines = CsvFileHelper_.readFile(file);
		System.out.println("Hoho : "+lines.size());
		data = new ArrayList<String[]>(lines.size());
		String sep = new Character(SEPARATOR).toString();
		
		for(String line : lines){	
			// First line of a CSV file defines the column names
			if(line.startsWith("#")){
				continue;
			}			
			System.out.println(line);
			String[] oneData = line.split(sep);
			data.add(oneData);						
			
			if((DEBUG_MODE)) System.out.println("Data size: "+ data.size());
			
			FILE_NB_ENTRIES = data.size();
		}		
	}
	
	/**
	 * save the data to Parse database
	 */
	public void saveToParse(){
		if(DEBUG_MODE) System.out.println("---- Entering SaveToParse method...");
		
		new ArrayList<SimpleMeasurement>();
		
		List<SimpleMeasurement> allMeasurements = new ArrayList<SimpleMeasurement>();
		List <Measurements> parseMeasurements = new ArrayList<Measurements>();
		
		allMeasurements = findAllNewMeasurements();
		
		// when the application has local entries
		ParseQuery<Measurements> query = ParseQuery.getQuery(Measurements.class);
		query.fromLocalDatastore();
		query.selectKeys(Arrays.asList("EntryId"));
		List<Measurements> result = null;
		
		try {
			result = query.find();
		} catch (ParseException e) {
			if(DEBUG_MODE) System.out.println("Query has not found anything");
			e.printStackTrace();
		}
	
		if(DEBUG_MODE) System.out.println("Result size: "+result.size());
		for(int m = 0; m < result.size();m++) {
			if(DEBUG_MODE) System.out.println("Query Result: "+(result.get(m)).getEntryId());
		}
		
		// save all measurements 
		for(int i = 0; i< allMeasurements.size(); i++){
			if(DEBUG_MODE) {
				System.out.println("i: "+i);
				System.out.println("AllMeasurements.size: " + allMeasurements.size());
			}
			measurements = new Measurements();
			measurements.setUuidString();
			measurements.setEntryId(allMeasurements.get(i).getEntryId());
			measurements.setInputId(allMeasurements.get(i).getIdPan());
			measurements.setI(allMeasurements.get(i).getI());
			measurements.setV(allMeasurements.get(i).getV());
			measurements.setLum(allMeasurements.get(i).getLum());
			measurements.setTair(allMeasurements.get(i).getTair());
			measurements.setSwind(allMeasurements.get(i).getSwind());
			measurements.setP(allMeasurements.get(i).getP());
			measurements.setTpan(allMeasurements.get(i).getTpan());
			measurements.setDate(allMeasurements.get(i).getTimeStamp());
			measurements.setGPS(allMeasurements.get(i).getGPS());	
			measurements.setDraft(true);
			measurements.setAuthor(ParseUser.getCurrentUser());
			
			if(DEBUG_MODE) System.out.println("result.size: "+result.size());
			
			boolean newEntry = true;
			for(int n = 0; n < result.size(); n++) {
				if(result.get(n).getEntryId() == measurements.getEntryId()) {
					if(DEBUG_MODE) System.out.println("Measurement "+ result.get(n).getEntryId() +" already exists");
					newEntry = false;
				}
			}
			// add the measurement in memory
			if(newEntry) {
				parseMeasurements.add(measurements);
				if(DEBUG_MODE) System.out.println("measurement "+ measurements.getEntryId()+" added");
			}
		}
		//ParseObject.saveAllInBackground(parseMeasurements);
		ParseObject.pinAllInBackground(parseMeasurements);
		Measurements.pinAllInBackground(PFE_ROVER_PVT_INES_ECE_Application.MEASUREMENTS_GROUP_NAME, parseMeasurements);
		syncMeasurementssToParse();
		
	}
	
	/**
	 * Converts the loaded data from String to SimpleMeasurement type
	 * @return  measurements - List
	 */
	@SuppressWarnings("unused")
	private List<SimpleMeasurement> findAllMeasurements(){
		if(DEBUG_MODE) System.out.println("---- Entering findAllMeasurements method...");
		
		final List<SimpleMeasurement> sms = new ArrayList<SimpleMeasurement>();
		
		for(String[] oneData : data){
			final SimpleMeasurement sm = tabToMeasurement(oneData);
			sms.add(sm);		
		}
		return sms;
	}

	/**
	 * Utility function to count the number of object saved in local memory
	 * @param count
	 * @return nb entries
	 */
	private int objectsWereCounted(int count) {
		if(DEBUG_MODE) System.out.println("Objects counted: "+count);
		return count;
	}
	
	/**
	 * Utility function to count the number of object saved in local memory
	 */
	private void objectCountFailed() {
		System.out.println("Object count failed");
	}
	
	/**
	 * Converts the loaded data from String to SimpleMeasurement type
	 * @return  measurements - List
	 */
	private List<SimpleMeasurement> findAllNewMeasurements(){
		if(DEBUG_MODE) System.out.println("---- Entering findAllMeasurements method...");
		
		final List<SimpleMeasurement> sms = new ArrayList<SimpleMeasurement>();
		if(DEBUG_MODE) {
			System.out.println("data size: "+data.size());
			System.out.println("Entry counter: "+ENTRY_COUNTER);
		}
		
		ParseQuery<Measurements> query = ParseQuery.getQuery("Measurements");
		//query.fromLocalDatastore();
		query.countInBackground(new CountCallback() {
			public void done(int count, ParseException e) {
				if(e == null) {
					objectsWereCounted(count);
				}else {
					objectCountFailed();
				}
			}
		});
				
		// if there is new data
		int nbNewEntry = 0;
		if(data.size() > ENTRY_COUNTER) {
			for(nbNewEntry = 0; nbNewEntry<data.size(); nbNewEntry++){
				SimpleMeasurement sm = tabToMeasurement(data.get(nbNewEntry));
				sms.add(sm);
				ENTRY_COUNTER++;
			}
			Toast.makeText(getApplicationContext(),nbNewEntry+" new entries saved ",	Toast.LENGTH_LONG).show();
		}else{
			Toast.makeText(getApplicationContext(),"there's No new entry ",	Toast.LENGTH_LONG).show();
		}
		return sms;
	}
	
	/**
	 * Map the loaded file entries with an object
	 * @param tab
	 * @return a measurement
	 */
	private SimpleMeasurement tabToMeasurement(String[] tab){
		if(DEBUG_MODE) System.out.println("---- Entering TabToMeasurements method...");
		
		SimpleMeasurement sm = new SimpleMeasurement();
		sm.setEntryId(Integer.parseInt(tab[10]));
		sm.setIdPan(Integer.parseInt(tab[0]));
		sm.setI(Double.parseDouble(tab[1]));
		sm.setV(Double.parseDouble(tab[2]));
		sm.setLum(Double.parseDouble(tab[3]));
		sm.setTair(Double.parseDouble(tab[4]));
		sm.setSwind(Double.parseDouble(tab[5]));
		sm.setP(Double.parseDouble(tab[6]));
		sm.setTpan(Double.parseDouble(tab[7]));
		sm.setTimeStamp(Long.parseLong(tab[8]));
		sm.setGPS(tab[9]);
		
		if(DEBUG_MODE) {
			for(int i = 0; i < tab.length; i++)
				System.out.println(tab[i]);
		}
		
		return sm;
	}
	
	/**
	 * Load local changes to Parse online server
	 */
	private void syncMeasurementssToParse() {
		
		if(DEBUG_MODE) System.out.println("---- Entering syncMeasurementsstoParse method...");
		
		// We could use saveEventually here, but we want to have some UI
		// around whether or not the draft has been saved to Parse
		ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo ni = cm.getActiveNetworkInfo();
		if ((ni != null) && (ni.isConnected())) {			
			if (!ParseAnonymousUtils.isLinked(ParseUser.getCurrentUser())) {
				// If we have a network connection and a current logged in user,
				// sync the
				// Measurements

				// In this app, local changes should overwrite content on the
				// server.	
				
				//TODO
				// check if entryId already exists in the database
				ParseQuery<Measurements> queryParse = ParseQuery.getQuery(Measurements.class);				
				queryParse.selectKeys(Arrays.asList("EntryId"));
				List<Measurements> result = null;
				try {
					result = queryParse.find();
				} catch (ParseException e) {
					if(DEBUG_MODE) System.out.println("Query has not found anything");
					e.printStackTrace();
				}
				
				//TODO
				//Retrieve measurements from local dataStore in addition or in place 
				//of from pin.
				ParseQuery<Measurements> query = Measurements.getQuery();
				query.fromPin(PFE_ROVER_PVT_INES_ECE_Application.MEASUREMENTS_GROUP_NAME);
				query.whereEqualTo("isDraft", true);
				query.whereNotContainedIn("EntryId", result);				
				query.findInBackground(new FindCallback<Measurements>() {					
					public void done(List<Measurements> Measurementss, ParseException e) {						
						if (e == null) {							
							if(DEBUG_MODE) System.out.println("SyncMeasurements local measurements size: "+Measurementss.size());
							for (final Measurements Measurements : Measurementss) {
								// Set is draft flag to false before
								// syncing to Parse								
								Measurements.setDraft(false);
								Measurements.getACL().setPublicReadAccess(PUBLIC_READ_ACCESS);
								Measurements.saveInBackground(new SaveCallback() {

									@Override
									public void done(ParseException e) {
										if (e == null) {
											// Let adapter know to update view
											if (!isFinishing()) {												
												if(DEBUG_MODE) System.out.println("sync with parse success case");
											}
										} else {
											// Reset the is draft flag locally
											// to true
											Measurements.setDraft(true);	
											if(DEBUG_MODE) System.out.println("sync with parse false case");
										}
									}
								});
							}							
						} else {	
							Log.i("MeasurementsListActivity",
									"syncMeasurementssToParse: Error finding pinned Measurementss: "
											+ e.getMessage());
						}
					}
				});
			} else {
				// If we have a network connection but no logged in user, direct
				// the person to log in or sign up.
				ParseLoginBuilder builder = new ParseLoginBuilder(this);
				startActivityForResult(builder.build(), LOGIN_ACTIVITY_CODE);				
			}
		} else {
			// If there is no connection, let the user know the sync didn't
			// happen
			Toast.makeText(
					getApplicationContext(),
					"Your device appears to be offline. Some Measurementss may not have been synced to Parse.",
					Toast.LENGTH_LONG).show();
		}
	}
	
	@Override
	public void onResume() {
		super.onResume();
		
		syncMeasurementssToParse();
	}

}